﻿namespace NJQuery {
	using System;
	using System.ComponentModel;
	using System.ComponentModel.Design.Serialization;
	using System.Globalization;
	using System.Reflection;
	using System.Collections;

	/// <summary>
	/// Provides conversions for the <see cref="Position"/> struct.
	/// </summary>
	public class PositionConverter : TypeConverter {


		#region Static Methods

		private static InstanceDescriptor GetInstanceDescriptor(Position value) {

			// Handle Shorthand
			if(value == Position.Center) return new InstanceDescriptor(
				typeof(Position).GetField("Center", BindingFlags.Public | BindingFlags.Static),
				null, true);
			if(value == Position.Top) return new InstanceDescriptor(
				typeof(Position).GetField("Top", BindingFlags.Public | BindingFlags.Static),
				null, true);
			if(value == Position.Right) return new InstanceDescriptor(
				typeof(Position).GetField("Right", BindingFlags.Public | BindingFlags.Static),
				null, true);
			if(value == Position.Bottom) return new InstanceDescriptor(
				typeof(Position).GetField("Bottom", BindingFlags.Public | BindingFlags.Static),
				null, true);
			if(value == Position.Left) return new InstanceDescriptor(
				typeof(Position).GetField("Left", BindingFlags.Public | BindingFlags.Static),
				null, true);

			return new InstanceDescriptor(
				typeof(Position).GetConstructor(new Type[] { typeof(HorizontalPosition), typeof(VerticalPosition) }),
				new object[] { value.Horizontal, value.Vertical });
		}

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new <see cref="PositionConverter"/> class.
		/// </summary>
		public PositionConverter() { }

		#endregion


		#region Methods: Capabilities

		/// <summary>
		/// Returns whether or not this object supports a standard set of values that can be picked from a list, using the specified context.
		/// </summary>
		/// <param name="context">A <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
		/// <returns>Whether or not a standard set of values is supported.</returns>
		public override bool GetStandardValuesSupported(ITypeDescriptorContext context) {
			return true;
		}

		/// <summary>
		/// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context.
		/// </summary>
		/// <param name="context">A <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
		/// <param name="sourceType">The type of the object to be converted.</param>
		/// <returns>Whether or not the conversion can be performed.</returns>
		public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
			if(sourceType == typeof(string)) return true;
			return base.CanConvertFrom(context, sourceType);
		}

		/// <summary>
		/// Returns whether this converter can convert the object to the specified type, using the specified context.
		/// </summary>
		/// <param name="context">A <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
		/// <param name="destinationType">The target type of the object.</param>
		/// <returns>Whether or not the conversion can be performed.</returns>
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
			if(destinationType == typeof(string)) return true;
			if(destinationType == typeof(InstanceDescriptor)) return true;
			return base.CanConvertTo(context, destinationType);
		}

		#endregion


		#region Methods: Perform tasks

		/// <summary>
		/// Returns a collection of standard values for the data type this converter is designed for when provided with a format context.
		/// </summary>
		/// <param name="context">A <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
		/// <returns>A collection of standard values.</returns>
		public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
			return new StandardValuesCollection(new Position[] {
				Position.Center,
				Position.Top,
				Position.Right,
				Position.Bottom,
				Position.Left,
				Position.TopRight,
				Position.BottomRight,
				Position.BottomLeft,
				Position.TopRight,
				Position.TopLeft,
				Position.Origin
			});
		}

		/// <summary>
		/// Converts the given object to the type of this converter, using the specified context and culture information.
		/// </summary>
		/// <param name="context">A <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
		/// <param name="culture">The <see cref="CultureInfo"/> to use as the current culture.</param>
		/// <param name="value">The object to be converted</param>
		/// <returns>The converted object.</returns>
		public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {

			if(value == null) return new Position();

			string text = value as string;
			if(text != null) {
				Position position;
				if(Position.TryParse(text, out position)) return position;
				return null;
			}

			return base.ConvertFrom(context, culture, value);
		}

		/// <summary>
		/// Converts the given object value to the specified type, using the specified context and culture information.
		/// </summary>
		/// <param name="context">A <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
		/// <param name="culture">The <see cref="CultureInfo"/> to use as the current culture.</param>
		/// <param name="value">The object to be converted</param>
		/// <param name="destinationType">The <see cref="Type"/> to convert the value parameter to.</param>
		/// <returns>The converted object.</returns>
		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {

			if(destinationType == typeof(string))
				return ((Position)value).ToString();

			if(destinationType == typeof(InstanceDescriptor))
				return GetInstanceDescriptor((Position)value);

			return base.ConvertTo(context, culture, value, destinationType);
		}

		#endregion


	}

}